package com.yh.csx.event.store.gateway.project;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.yh.csx.event.domain.gateway.IProjectGateway;
import com.yh.csx.event.store.event.dto.req.EventReq;
import com.yh.csx.event.store.event.dto.resp.EventResp;
import com.yh.csx.event.store.gateway.project.dataobject.ProductConfigDo;
import com.yh.csx.event.store.gateway.project.dataobject.ProjectConfigDo;
import com.yh.csx.event.store.gateway.project.dataobject.TeamConfigDo;
import com.yh.csx.event.store.project.dto.resp.ProductResp;
import com.yh.csx.event.store.project.dto.resp.ProjectResp;
import com.yh.csx.event.store.project.dto.resp.TeamResp;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Example;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author zhangsheng
 * @date 2021/7/29 15:39
 */
@Slf4j
@Component
public class ProjectGateway implements IProjectGateway {

    private Cache<Long, ProjectConfigDo> projectCache;
    private Cache<Long, ProductConfigDo> productCache;
    private Cache<Long, TeamConfigDo> teamCache;
    private Cache<String, ProjectConfigDo> nameCache;
    private Cache<Long, List<TeamResp>> teamRespCache;
    @Value("${bsf.event.cache.expire.second:300}")
    private long expireSecond = 60 * 5;
    @Autowired
    private TeamRepository teamRepository;
    @Autowired
    private ProjectRepository projectRepository;
    @Autowired
    private ProductRepository productRepository;

    @PostConstruct
    public void init() {
        projectCache = CacheBuilder.newBuilder()
                .expireAfterAccess(expireSecond, TimeUnit.SECONDS)
                .build();
        productCache = CacheBuilder.newBuilder()
                .expireAfterAccess(expireSecond, TimeUnit.SECONDS)
                .build();
        teamCache = CacheBuilder.newBuilder()
                .expireAfterAccess(expireSecond, TimeUnit.SECONDS)
                .build();
        nameCache = CacheBuilder.newBuilder()
                .expireAfterAccess(expireSecond, TimeUnit.SECONDS)
                .build();
        teamRespCache = CacheBuilder.newBuilder()
                .expireAfterAccess(expireSecond, TimeUnit.SECONDS)
                .build();
    }

    public ProjectConfigDo getCacheProjectByName(String appName) {
        ProjectConfigDo config = new ProjectConfigDo();
        config.setId(0L);
        if (StringUtils.isEmpty(appName)) {
            return config;
        }
        try {
            return nameCache.get(appName, () -> {
                ProjectConfigDo projectByName = getProjectByName(appName);
                if (null != projectByName) {
                    return projectByName;
                }
                return config;
            });
        } catch (Exception e) {
            log.error("=getCacheProjectByName=>appName:{}", appName, e);
        }
        try {
            return getProjectByName(appName);
        } catch (Exception e) {
            log.error("=getCacheProjectByName=>appName:{}", appName, e);
            return config;
        }
    }

    private ProjectConfigDo getProjectByName(String appName) {
        ProjectConfigDo project = new ProjectConfigDo();
        project.setProjectBeName(appName);
        return projectRepository.findOne(Example.of(project)).orElseGet(() -> new ProjectConfigDo());
    }

    @Override
    public List<TeamResp> queryTeams() {
        try {
            return teamRespCache.get(0L, () -> teamRepository.findAll().stream().map(this::toTeam).collect(Collectors.toList()));
        } catch (ExecutionException e) {
        }
        return teamRepository.findAll().stream().map(this::toTeam).collect(Collectors.toList());
    }

    private TeamResp toTeam(TeamConfigDo team) {
        ProductConfigDo productConfigDo = new ProductConfigDo();
        productConfigDo.setTeamId(team.getId());
        List<ProductConfigDo> products = productRepository.findAll(Example.of(productConfigDo));
        products.forEach(pcf -> {
            try {
                productCache.get(pcf.getId(), () -> pcf);
            } catch (ExecutionException e) {
            }
        });

        return new TeamResp().setId(team.getId()).setName(team.getTeamName())
                .setProducts(products.stream().map(this::toProduct).collect(Collectors.toList()));
    }

    private ProductResp toProduct(ProductConfigDo product) {
        ProjectConfigDo projectConfigDo = new ProjectConfigDo();
        projectConfigDo.setProductId(product.getId());
        List<ProjectConfigDo> list = projectRepository.findAll(Example.of(projectConfigDo));
        list.forEach(pjc -> {
            try {
                projectCache.get(pjc.getId(), () -> pjc);
                nameCache.get(pjc.getProjectBeName(), () -> pjc);
            } catch (ExecutionException e) {
            }
        });
        return new ProductResp().setId(product.getId()).setName(product.getProductName())
                .setProjects(list.stream().map(this::toProject).collect(Collectors.toList()));
    }

    private ProjectResp toProject(ProjectConfigDo project) {
        return new ProjectResp().setId(project.getId()).setName(project.getProjectName());
    }

    public void configName(EventResp resp) {
        try {
            ProjectConfigDo projectConfigDo = projectCache.get(resp.getProjectId(),
                    () -> projectRepository.findById(resp.getProjectId()).orElseGet(() -> {
                        ProjectConfigDo project = new ProjectConfigDo();
                        project.setProductId(0L);
                        return project;
                    }));
            ProductConfigDo productConfigDo = productCache.get(projectConfigDo.getProductId(),
                    () -> productRepository.findById(projectConfigDo.getProductId()).orElseGet(() -> {
                        ProductConfigDo product = new ProductConfigDo();
                        product.setTeamId(0L);
                        return product;
                    }));
            resp.setProductName(productConfigDo.getProductName());
            TeamConfigDo teamConfigDo = teamCache.get(productConfigDo.getTeamId(),
                    () -> teamRepository.findById(productConfigDo.getTeamId()).orElseGet(() -> new TeamConfigDo()));
            resp.setTeamName(teamConfigDo.getTeamName());
        } catch (ExecutionException e) {
        }
    }
}
